#include "tour.h"

static sigjmp_buf jmpbuf;
static unsigned int datalen =56;

int add_to_tour_list(TOUR_DATA *tour_data, int index, char *node_name)
{
	struct hostent *node_host = NULL;	

	if ((node_host = gethostbyname(node_name)) == NULL) {
		printf("TOUR: tour node name not valid\n");
		return -1;
	}

	memcpy(&(tour_data->ip[index]), &((struct in_addr *)node_host->h_addr)->s_addr, sizeof(unsigned long)); 
        return 0;
}

int process_input_and_build_tour(int argc, char *argv[], TOUR_MODULE_VARS *global_vars) 
{
	int ret = 0, index = 1;
	TOUR_DATA *tour_data = NULL;

	/* There are a total of argc-1 paths to treverse */
	tour_data = malloc(sizeof(TOUR_DATA)); 

	if (!tour_data) {
		printf("TOUR: failed to malloc for tour_data\n");
		ret = -1;
		goto out;
	}

	memset(tour_data, 0, sizeof(TOUR_DATA));

	/*Add yourself as the first entry in the tour*/
	add_to_tour_list(tour_data, 0, global_vars->host_name);
	tour_data->max_index = 1;

	while (argv[index]) {
		if ((strlen(argv[index]) == strlen(global_vars->host_name))) {
                   if (!memcmp(global_vars->host_name, argv[index], strlen(argv[index]))) {
			printf("TOUR:source node part of the tour\n");
			ret = -1;
			goto out_free_tour;
                    }
		}		
		add_to_tour_list(tour_data, index, argv[index]);
		index++;
	}

	/*Add the multicast ip and the multicast port to the TOUR_DATA */
	memcpy(tour_data->mcast_ip, MULTICAST_ADDR, IP_SIZE);
	tour_data->mcast_port_no = MULTICAST_PORT;
	tour_data->max_index =  index-1;
	tour_data->cur_dest_index = 1;

	ret = do_start_tour(global_vars, tour_data);
	if (ret < 0) {
		printf("TOUR: failed to start tour\n");
		goto out_free_tour;
	}	

out_free_tour:
	free(tour_data);	
out:
	return ret;
}



int do_start_tour(TOUR_MODULE_VARS *global_vars, TOUR_DATA *tour_data)
{
	int ret = 0;
	
		
	ret = build_and_send_ip_packet(global_vars, tour_data, 0, tour_data->cur_dest_index);
	if (ret < 0) {
		printf("TOUR: %s:%d error in sending the ip packet \n", __func__, __LINE__);
		return ret;
	}

	return ret;
}


int build_and_send_ip_packet(TOUR_MODULE_VARS *global_vars, TOUR_DATA *tour_data, int src_index, int dest_index)
{
	int ret = 0;
	struct ip *ip_header = NULL;
	char *buf = NULL;
	unsigned int len = 0;
	struct sockaddr_in dest;	

	len = sizeof(struct ip) + sizeof(TOUR_DATA);
	buf = malloc(sizeof(char)*len);

	if (!buf) {
		printf("Failed to malloc buffer for packet to be sent out\n");
		ret = -1;
		goto out;
	}

	memset(buf, 0, sizeof(char)*len);

	dest.sin_family = AF_INET;
	dest.sin_addr.s_addr = tour_data->ip[dest_index];
	
	/*
	 * Build the IP packet with all the relevant information 
	 * First build the IP header: Everything has to be stored in the network
	 * byte order.  
	 */
	ip_header = (struct ip *)buf;
	ip_header->ip_v = IPVERSION;
	ip_header->ip_hl = sizeof(struct ip) >> 2;
        ip_header->ip_p = IPPROTO_TOUR;
	ip_header->ip_tos = 0;
	ip_header->ip_len = htons(len); 
	ip_header->ip_id = htons(IP_ID_VAL);
	ip_header->ip_off = 0;
	ip_header->ip_ttl = TTL_OUT;
	ip_header->ip_src.s_addr = tour_data->ip[src_index];
	ip_header->ip_dst.s_addr = tour_data->ip[dest_index];
        ip_header->ip_sum  = 0;
        
	memcpy(buf+sizeof(struct ip), tour_data, sizeof(TOUR_DATA));

        ip_header->ip_sum = in_cksum ((uint16_t *) ip_header, len);

	ret = sendto(global_vars->rt_sock, buf, len, 0, (struct sockaddr *)&dest, sizeof(dest));
	if (ret < 0) {
		printf("Failed to send the tour packet out: error %s\n", strerror(errno));
		free(buf);
		goto out;
	}
		
out:
	return ret;
}


int handle_route_socket(TOUR_MODULE_VARS *tour_vars)
{
	int ret = 0;
	char *buf = NULL;
	socklen_t addrlen;
	struct ip *ip_header = NULL;
	int first_time = 0, is_destination = 0;
	char time_buff[TIME_BUF_SIZE] = {0};
	time_t ticks;
	uint32_t source_addr = 0, tour_start_source = 0;
	TOUR_DATA *tour_data = NULL;
	struct sockaddr_in source;
        char *tour_source_name = NULL;
        char tour_source_ip[INET_ADDRSTRLEN] = {0};


	buf = malloc(sizeof(char)*MAX_PACKET_SIZE);
	if (!buf) {
		printf("%s:%d failed to malloc\n", __func__, __LINE__);
		return -1;
	}
	
	addrlen = sizeof(struct sockaddr_in);

	ret = recvfrom(tour_vars->rt_sock, buf, MAX_PACKET_SIZE, 0, (struct sockaddr *)&source, &addrlen); 

	if (ret < 0) {
		printf("%s:%d recvfrom failed with error %s\n", __func__, __LINE__, strerror(errno));
		return ret;
	}

	ip_header = (struct ip *)buf;
	tour_data = (TOUR_DATA *)(buf + sizeof(struct ip));

	if (ntohs(ip_header->ip_id) != IP_ID_VAL) {
		printf("%s:%d received an invalid packet on the rtsock \n", __func__, __LINE__);
		return -1;
	}

	ticks = time(NULL);

	snprintf(time_buff, sizeof(time_buff), "%.24s\r\n", ctime(&ticks));
	source_addr = ip_header->ip_src.s_addr;
	tour_start_source = tour_data->ip[0]; /* Contains the address of the tour started */

	printf("%s: Received source routing packet from %s\n",time_buff, get_host_name_with_netaddr(source_addr));

	first_time = check_if_pkt_is_first_time(tour_vars, tour_start_source);
	if (tour_data->cur_dest_index == tour_data->max_index) {
		is_destination = 1;
		tour_vars->is_destination = 1;
	}

	if (first_time) {
		ret = join_multicast_group(tour_vars->mcast_sock, tour_data->mcast_ip);
		memcpy(tour_vars->mcast_ip, tour_data->mcast_ip, IP_SIZE);
		tour_vars->mcast_port_no = tour_data->mcast_port_no;
		tour_vars->tour_src_addr = tour_start_source;
		tour_vars->send_ping = 1;
                tour_source_name = get_host_name_with_netaddr(tour_start_source);
                inet_ntop(AF_INET, &tour_start_source, tour_source_ip, INET_ADDRSTRLEN);
                printf("PING %s(%s): %d data bytes\n", tour_source_name?tour_source_name:tour_source_ip, tour_source_ip, datalen);
	}
	
	if (!is_destination) {
		ret = update_and_send_rt_packet(tour_vars, buf);

		if (ret < 0) {
			printf("%s:%d failed to update and send source route packet\n", __func__, __LINE__);
			free(buf);
			return ret;
		}
	} else {
		if(!first_time) {
			tour_vars->send_ping = 0;
			sleep(5);
			begin_mcast(tour_vars);
		}
	}

	free(buf);
	return ret;
}

int begin_mcast(TOUR_MODULE_VARS *tour_vars)
{
	char msg[MESSAGE_SIZE];

	sprintf(msg,"<<<This is node %s. Tour has ended. Group members please identify yourselves>>>", tour_vars->host_name);

	return send_multicast_message(tour_vars->mcast_sock, msg, tour_vars->mcast_ip, tour_vars->mcast_port_no, tour_vars);
}

int update_and_send_rt_packet(TOUR_MODULE_VARS *tour_vars, char *buf)
{
	int ret = 0, src_index = 0, dest_index = 0;
	TOUR_DATA *tour_data =  NULL;
	
	/* The data starts after the ip_header */
	tour_data =(TOUR_DATA *)(buf + sizeof(struct ip));
	src_index = tour_data->cur_dest_index;
	dest_index = src_index + 1;
        tour_data->cur_dest_index = dest_index;

	ret =  build_and_send_ip_packet(tour_vars, tour_data, src_index, dest_index);
	if (ret < 0) {
		printf("Failed to forward the tour packet out\n");
		return -1;
	}

	return ret;
}


void  *do_start_pinging(void *arg)
{
        TOUR_MODULE_VARS *tour_vars = (TOUR_MODULE_VARS *)arg;
	int ret = 0, i = 0;
        unsigned int  buf_len = 1500, icmp_cksum_len = 0, total_len = 0, ip_cksum_len = 0;
	unsigned char src_mac[ETH_ALEN];
	char *buf = NULL;
	struct ethhdr *eh = NULL;
	struct icmp *icmp_header = NULL;
	struct ip *ip_header = NULL;
	struct sockaddr_ll socket_address;
	struct sockaddr_in dest_ip_addr;
	socklen_t sockaddrlen = sizeof(struct sockaddr_in);
	struct hwaddr HWaddr;
	uint32_t saddr, daddr = tour_vars->tour_src_addr;
	
	HWaddr.sll_ifindex = 2;
	HWaddr.sll_hatype = ARPHRD_ETHER;
	HWaddr.sll_halen  = ETH_ALEN;
	memset(&HWaddr.sll_addr, 0, sizeof(HWaddr.sll_addr));
        
	dest_ip_addr.sin_family = AF_INET;
	dest_ip_addr.sin_addr.s_addr = daddr; 

	ret = areq((struct sockaddr *)&dest_ip_addr, sockaddrlen, &HWaddr);
	if (ret < 0) {
		printf("Failed to get the destination mac for the source address\n");
                goto out_thread;
                
	}

	buf = malloc(buf_len);
	if (!buf) {
		printf("%s:%d failed to malloc buffer to send the ping packet out \n",__func__, __LINE__);
                goto out_thread;
	}

        memset(buf, 0, buf_len);
        
        /* Get the source mac and the source ip */
        get_src_mac(src_mac);

	inet_pton(AF_INET, tour_vars->host_ip, &saddr);
        
        socket_address.sll_family   = PF_PACKET;        
        socket_address.sll_protocol = htons(ETH_P_IP);
        socket_address.sll_ifindex  = 2;
        socket_address.sll_hatype   = ARPHRD_ETHER;
        socket_address.sll_pkttype  = PACKET_OTHERHOST;
        socket_address.sll_halen    = ETH_ALEN;

        for (i = 0; i < ETH_ALEN;i++) {
                socket_address.sll_addr[i]  = HWaddr.sll_addr[i];
        }
        socket_address.sll_addr[6]  = 0x00;
        socket_address.sll_addr[7]  = 0x00;

	eh = (struct ethhdr *)buf;
        /*Copy the destination address */
        memcpy((void*)buf, (void*)HWaddr.sll_addr, ETH_ALEN);
        /*Copy the source address */
        memcpy((void*)(buf+ETH_ALEN), (void*)src_mac, ETH_ALEN);

        eh->h_proto = htons(ETH_P_IP);

        
	/* Fill the IP header */
	ip_header = (struct ip *)(buf + sizeof(struct ethhdr));

	ip_header->ip_v = IPVERSION;
	ip_header->ip_hl = sizeof(struct ip) >> 2;
	ip_header->ip_p = IPPROTO_ICMP;
        ip_header->ip_tos = 0;
        ip_cksum_len = sizeof(struct ip) + 8 + datalen; 
	ip_header->ip_len = htons(ip_cksum_len);
        ip_header->ip_sum = 0;
	ip_header->ip_id = 0; 
	ip_header->ip_off = 0;
	ip_header->ip_ttl = TTL_OUT;
	ip_header->ip_src.s_addr = saddr;
	ip_header->ip_dst.s_addr = daddr;

	/* Fill ICMP header */
	icmp_header = (struct icmp *)(buf + sizeof(struct ethhdr) + sizeof(struct ip));
	icmp_header->icmp_type = ICMP_ECHO;
	icmp_header->icmp_code = 0;
	icmp_header->icmp_id = tour_vars->pid;
	icmp_header->icmp_seq = tour_vars->ping_sequence_no++;
        
	memset(icmp_header->icmp_data, 0xa5, datalen);
	gettimeofday((struct timeval *) icmp_header->icmp_data, NULL);

	icmp_header->icmp_cksum = 0;
	icmp_cksum_len = 8 + datalen;
        /* Calculating the icmp checksum */
	icmp_header->icmp_cksum = in_cksum((uint16_t *)icmp_header, icmp_cksum_len);

        /* Calculating the ip checksum */	
        ip_header->ip_sum = in_cksum ((uint16_t *) ip_header, ip_cksum_len); 

        total_len = sizeof(struct ethhdr) + sizeof(struct ip) + 8 + datalen;

	/*send the packet*/
        ret = sendto(tour_vars->pgout_sock, buf, total_len, 0,
                     (struct sockaddr*)&socket_address, sizeof(socket_address));

        if (ret < 0) {
                printf("TOUR: Sending the ping ethernet frame failed with %s\n", strerror(errno));
                ret = -1;
        }
	free(buf);
out_thread:
        pthread_detach(pthread_self());
	return NULL;
}

/* Flicked from UNP/stevens */
uint16_t in_cksum(uint16_t *addr, int len)
{
	int nleft = len;
	uint32_t sum = 0;
	uint16_t *w = addr;
	uint16_t answer = 0;

	while (nleft > 1) {
		sum += *w++;
		nleft -= 2;
	}

	if (nleft == 1) {
		*(unsigned char *) (&answer) = *(unsigned char *) w;
		sum += answer;
	}

	sum = (sum >> 16) + (sum & 0xffff);
	sum += (sum >> 16);
	answer = ~sum;
	return (answer);
}

char *get_host_name_with_netaddr(uint32_t addr) 
{
    struct hostent *host = NULL;

    if ((host = gethostbyaddr(&(addr), 4 , AF_INET)) == NULL) {
        printf("TOUR - %s: gethostbyaddr for the IP failed\n", __func__);
    }
    return host->h_name;
}

int check_if_pkt_is_first_time(TOUR_MODULE_VARS *global_vars, uint32_t saddr) 
{

	if (global_vars->tour_src_addr == saddr) {
		printf("%s:%d this node has already been visited in the tour\n", __func__, __LINE__);
		return 0;
	}
	return 1;
}

static int close_vars(TOUR_MODULE_VARS *vars)
{
	close(vars->rt_sock);
	close(vars->pgin_sock);
	close(vars->pgout_sock);
	close(vars->mcast_sock);
}

static int init_vars(TOUR_MODULE_VARS *vars, int argc)
{
        int val = 1, ret = 0;
        struct sockaddr_in addr;
        struct hostent *host_entry;

        if(gethostname(vars->host_name, sizeof(vars->host_name)) < 0) {
                printf("Error getting the hostname:%s", __func__);
                return 1;
        }

        if( (host_entry = gethostbyname(vars->host_name)) == NULL) {
                printf("Error getting the IP address of the host:%s", __func__);
                return 1;
        }

        inet_ntop(AF_INET, ((struct in_addr *)host_entry->h_addr),vars->host_ip, IP_SIZE);

        /* Creating the IP Raw socket for sending out tour messages */
        vars->rt_sock = socket(AF_INET, SOCK_RAW, IPPROTO_TOUR);
        if(vars->rt_sock < 0) {
                printf("Error creating the rt_sock\n:%s",__func__);
                return 1;
        }

        if(setsockopt(vars->rt_sock, IPPROTO_IP, IP_HDRINCL, &val, sizeof(int)) < 0) {
                printf("Error: Cannot set IP_HDRINCL option:%s %s\n", __func__, strerror(errno));
                return 1;
        }

        /* Creating the IP Raw socket for receiving ping messages */
        if (argc == 1) {
                vars->pgin_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
                if(vars->pgin_sock < 0) {
                        printf("Error creating the pgin_sock\n:%s",__func__);
                        return 1;
                }
        }

        /* Creating the PF_PACKET for sending ping messages */
        vars->pgout_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
        if(vars->pgout_sock < 0) {
                printf("Error creating the pgout_sock\n:%s",__func__);
                return 1;
        }

        /* Creating the UDP socket for multicast */
        vars->mcast_sock = socket(AF_INET, SOCK_DGRAM, 0);
        if(vars->mcast_sock < 0) {
                printf("Error creating the mcast_sock\n:%s",__func__);
                return 1;
        }

        /* Setting the socketopt SO_REUSEADDR so that multiple
         * instances of this application can bind to the same port
         */
        val = 1;
        if (setsockopt(vars->mcast_sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
                printf("Reusing ADDR failed:%s", __func__);
                return 1;
        }

        u_char ttl = 1;
        if (setsockopt(vars->mcast_sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
                printf("Failed to set multicast TTL:%s", __func__);
                return 1;
        }

        /* Binding the UDP socket to the multicast port to receive
         *  UDP packets only from that port
         */
        memset(&addr,0,sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        addr.sin_port = htons(MULTICAST_PORT);

        if(bind(vars->mcast_sock, (struct sockaddr *)&addr,sizeof(addr)) <0 ) {
                printf("Error while binding multicast socket:%s", __func__);
                return 1;
        }

        /* Joining the Multicast group specified 
         *  by MULTICAST_ADDR
         */
        if (argc > 1) {
                ret = join_multicast_group(vars->mcast_sock, MULTICAST_ADDR);
                /* 
                 * Store the mulitcast address and port number in the 
                 * tour gloabal variables only if we are
                 * the source node
                 */
                memcpy(vars->mcast_ip, MULTICAST_ADDR , IP_SIZE);
                vars->mcast_port_no = MULTICAST_PORT;
        }

	vars->pid = getpid() & 0xffff;
	vars->send_ping = 0;
	vars->ping_sequence_no = 1;
	return ret;
}

void sig_alrm(int signo)
{
	siglongjmp(jmpbuf, 1);
}


int join_multicast_group(int sockfd, char *mcast_addr)
{
	struct ip_mreq mreq;

	/* Joining the Multicast group specified 
	*  by mcast_addr
	*/
        inet_pton(AF_INET, mcast_addr, &mreq.imr_multiaddr);
	mreq.imr_interface.s_addr=htonl(INADDR_ANY);

	if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
		printf("setsockopt failed while joining multicast group:%s - %s", __func__, strerror(errno));
		return 1;
	}
	return 0;
}

int send_multicast_message(int sockfd, char *msg, char *mcast_addr, unsigned int port, TOUR_MODULE_VARS *vars)
{
	struct sockaddr_in addr;

	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
        inet_pton(AF_INET, mcast_addr, &addr.sin_addr);
	addr.sin_port = htons(port);
        
        printf("Node %s. Sending: %s\n", vars->host_name, msg);

	if(sendto(sockfd, msg, strlen(msg)+1, 0, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
		printf("Error sending mulitcast message:%s with error %s\n",__func__, strerror(errno));
		return -1;
	}
	return 0;
}


void tv_sub(struct timeval *out, struct timeval *in)
{
	if((out->tv_usec -= in->tv_usec) < 0) {
		--out->tv_sec;
		out->tv_usec += 1000000;
	}
	out->tv_sec -= in->tv_sec;
}

void handle_pgin_sock(TOUR_MODULE_VARS *vars)
{
	struct ip *ip;
	struct icmp *icmp;
	int hlen, icmplen, n;
	unsigned int addrlen;
	double rtt;
	struct timeval *tvsend, tvrecv;
	char buf[MESSAGE_SIZE];
	struct sockaddr_in addr;
	char recv_ip[IP_SIZE];
                
	addrlen = sizeof(addr);
	n = recvfrom(vars->pgin_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, &addrlen);
	if(n < 0) {
		printf("Error reading from the pgin socket:%s %s\n", __func__, strerror(errno));
		return;
	}

	ip = (struct ip*)buf;
	hlen = ip->ip_hl << 2;

	icmp = (struct icmp *)(buf + hlen);	
	if((icmplen = n - hlen) < 8) {
		printf("Error in icmphdr length:%d\n", icmplen);
		return;
	}

	if(icmp->icmp_type == ICMP_ECHOREPLY) {
		if(icmp->icmp_id != vars->pid) {
			return;	
		}

		if(vars->is_destination == 1)
			vars->echo_reply_count++;
	
		gettimeofday(&tvrecv, NULL);
		tvsend = (struct timeval*)icmp->icmp_data;
		tv_sub(&tvrecv, tvsend);
		rtt = tvrecv.tv_sec * 1000.0 + tvrecv.tv_usec/1000.0;
		inet_ntop(AF_INET, &addr.sin_addr, recv_ip, IP_SIZE);

		printf("Received %d bytes from %s: seq = %u, ttl = %d, rtt =%.3f ms\n",
			icmplen, recv_ip, icmp->icmp_seq, ip->ip_ttl, rtt);

		if(vars->echo_reply_count == MAX_WAIT_ECHO_REPLIES) {
			vars->send_ping = 0;
			begin_mcast(vars);
		}
	}
}

int handle_mcast_sock(TOUR_MODULE_VARS *vars)
{
	int n;
	unsigned int addrlen;
	struct sockaddr_in addr;
	char msg[MULTICAST_MSG_SIZE];	
	char mcast_addr[IP_SIZE];
	struct itimerval timer;

	memset(&timer, 0, sizeof(timer));
	addrlen = sizeof(addr);
	n = recvfrom(vars->mcast_sock, msg, MULTICAST_MSG_SIZE, 0, (struct sockaddr*) &addr, &addrlen);

	if(n < 0) {
		printf("Error reading from mulitcast socket:%s\n", __func__);
		return n;
	}

	/* Print the messge out in the required form */
	printf("Node %s. Received: %s\n", vars->host_name, msg);
	
	vars->send_ping = 0;
	memset(msg, 0, MULTICAST_MSG_SIZE);
	sprintf(msg, "Node %s. I am a member of the group.", vars->host_name);

	inet_ntop(AF_INET, &addr.sin_addr, mcast_addr, IP_SIZE);
	send_multicast_message(vars->mcast_sock, msg, MULTICAST_ADDR, MULTICAST_PORT,/*vars->mcast_ip, vars->mcast_port_no*/ vars);

	/* Start a timer for 5 seconds to receive the second multicast message */
	signal(SIGALRM, sig_alrm);

wait_for_mcast:
	timer.it_value.tv_sec = MULTICAST_TIMEOUT;
	setitimer(ITIMER_REAL, &timer, NULL);

	if(sigsetjmp(jmpbuf, 1) != 0) {
		printf("Timed out waiting for second multicast message, Exiting TOUR application\n");
		return -1;
	}
	
	n = recvfrom(vars->mcast_sock, msg, MULTICAST_MSG_SIZE, 0, (struct sockaddr*) &addr, &addrlen);
	
	timer.it_value.tv_sec = 0;
	setitimer(ITIMER_REAL, &timer, NULL);	
	if(n < 0) {
		printf("Error reading from mulitcast socket:%s\n", __func__);
		return n;
	}
	
	/* Print the messge out in the required form */
	printf("Node %s. Received: %s\n", vars->host_name, msg);
	goto wait_for_mcast;
}

int main(int argc, char* argv[])
{
	TOUR_MODULE_VARS vars;
	fd_set allset, rset;
	int maxfd, n = -1, ret = 0;
	struct timeval timer;
	int timer_on = 0;
        pthread_t tid = 0;

	memset(&vars, 0, sizeof(vars));

	if(init_vars(&vars, argc) != 0) {
                printf("Failed to intialize the global variables\n");
		return 1;
        }
        
        if (argc > 1) {
                 ret = process_input_and_build_tour(argc, argv, &vars);
                 if (ret < 0) {
                        printf("Failed to build the tour list\n");
                        exit(-1);
                 }
        }

	FD_ZERO(&allset);
	FD_SET(vars.rt_sock, &allset);
	FD_SET(vars.pgin_sock, &allset);
	FD_SET(vars.mcast_sock, &allset);
	maxfd = vars.mcast_sock;
	
	timer.tv_sec = PING_TIMEOUT;
	timer.tv_usec = 0;

        Get_hw_addrs();

	for( ; ; ) {
		rset = allset;
		if(timer_on == 0)
			select(maxfd +1, &rset, NULL, NULL, NULL);
		else
			n = select(maxfd+1, &rset, NULL, NULL, &timer);
	
		if (n==0) {
			if(vars.send_ping == 1) {
				printf("Timeout occurred: Send ping packet:%s\n", __func__);
				/* send_ping_packet() */
                                if (pthread_create(&tid, NULL, do_start_pinging, (void *)&vars) != 0) {
                                        printf("ERROR : thread creation failed exiting client\n");
                                        return -1;
                                }
				timer.tv_sec = PING_TIMEOUT;
			}
		}
		if(FD_ISSET(vars.rt_sock, &rset)) {
			handle_route_socket(&vars);
			timer_on = 1;
		}

		if(FD_ISSET(vars.pgin_sock, &rset)) {
			handle_pgin_sock(&vars);
		}

		if(FD_ISSET(vars.mcast_sock, &rset)) {
			if(handle_mcast_sock(&vars) < 0)
				goto end;
		}
	}	 
end:
	close_vars(&vars);
	return 0;
}

int areq(struct sockaddr *IPaddr, socklen_t sockaddrlen, struct hwaddr *HWaddr)
{
    int sockfd;
    struct sockaddr_un domaddr;
    char *buf;
    fd_set readfds;
    struct timeval timeout;
    int ret = 0;

    buf = malloc(sizeof(struct sockaddr) + sizeof(struct hwaddr));

    if((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
        fprintf(stderr, "ARP: Error in creating DOMAIN socket - %s\n", strerror(errno));
        ret = sockfd;
        goto out;
    }

    memset(&domaddr, 0, sizeof(domaddr));
    domaddr.sun_family = AF_LOCAL;
    strcpy(domaddr.sun_path, DOM_SOCK_FILE);

    if(connect(sockfd, (struct sockaddr *)&domaddr, sizeof(struct sockaddr)) < 0) {
        fprintf(stderr, "TOUR: Error connecting to ARP listening socket - %s\n", strerror(errno));
        ret = -1;
        goto out_file;
    }

    memcpy(buf, IPaddr, sockaddrlen);
    memcpy(buf + sockaddrlen, HWaddr, sizeof(struct hwaddr));

    if(write(sockfd, buf, sockaddrlen + sizeof(struct hwaddr)) < 0) { 
        fprintf(stderr, "TOUR: Error sending IPaddr to ARP listening socket - %s\n", strerror(errno));
        ret = -1;
        goto out_file;
    }

    FD_ZERO(&readfds);
    FD_SET(sockfd, &readfds);

    timeout.tv_sec = 10;  /* 10 secs timeout */
    timeout.tv_usec = 0;

    if((ret = select(sockfd + 1, &readfds, NULL, NULL, &timeout)) >= 0) {
        if(ret == 0) {
            printf("Timeout while awaiting a reply from ARP.\n");
            ret = -1;
            goto out_file;
        }

        if(FD_ISSET(sockfd, &readfds)) {
            ret = read(sockfd, HWaddr, sizeof(*HWaddr));
            if(ret <= 0) {
                ret = -1;
                fprintf(stderr, "TOUR: Error in receiving HWaddr from ARP - %s\n", strerror(errno));
                goto out_file;
            }
            ret = 0;
        }
    } else {
        fprintf(stderr, "TOUR: Error in select() - %s\n", strerror(errno));
        ret = -1;
        goto out_file;
    }

out_file:
    close(sockfd);
out:
    free(buf);
    return ret;
}

